package com.clp.protocol.iec104.server.pipeline.state;

import com.clp.protocol.iec104.server.InSlaveChannel;
import com.clp.protocol.iec104.server.pipeline.InternalIAsduSender;
import com.clp.protocol.iec104.server.pipeline.InternalSApduSender;
import com.clp.protocol.iec104.server.pipeline.InternalUApduSender;
import com.clp.protocol.iec104.server.pipeline.PipelineManager;
import com.clp.protocol.iec104.server.pipeline.state.control.ControlInfo;
import io.netty.channel.*;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Nullable;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

@Slf4j
public abstract class AbstractStateHandler extends ChannelDuplexHandler {
    private final PipelineManager manager;
    protected ChannelHandlerContext ctx;
    protected Channel channel;
    protected EventLoop eventLoop;

    /**
     * 定时任务future
     */
    @Nullable
    private volatile ScheduledFuture<?> scheduledFuture;
    @Nullable
    private final ScheduledTask task = getScheduledTask();

    protected AbstractStateHandler(PipelineManager manager) {
        this.manager = manager;
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        this.ctx = ctx;
        this.channel = ctx.channel();
        this.eventLoop = channel.eventLoop();
        resetState();
    }

    /**
     * 重置状态
     */
    protected abstract void resetState();

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        afterResetState();
        // 初始化监听任务
        if (task != null) {
            scheduledFuture = eventLoop.scheduleAtFixedRate(task, task.initialDelay, task.period, task.timeUnit);
        }

        ctx.fireChannelActive();
    }

    /**
     * 重置完状态之后，此时通道已经激活
     */
    protected abstract void afterResetState();

    protected InSlaveChannel inSlaveChannel() {
        return manager.getInSlaveChannel();
    }

    protected ControlInfo controlInfo() {
        return manager;
    }

    protected InternalUApduSender uApduSender() {
        return manager.getUApduSender();
    }

    protected InternalSApduSender sApduSender() {
        return manager.getSApduSender();
    }

    protected InternalIAsduSender iAsduSender() {
        return manager.getIAsduSender();
    }

    protected void safeExecute(Runnable task) {
        if (eventLoop.inEventLoop()) {
            task.run();
        } else {
            eventLoop.execute(task);
        }
    }

    @Nullable
    protected abstract ScheduledTask getScheduledTask();

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        if (scheduledFuture != null) {
            boolean isCanceled = scheduledFuture.cancel(false);
            if (!isCanceled) {
                log.warn("任务停止失败");
            }
            scheduledFuture = null;
        }

        ctx.fireChannelInactive();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
    }

    protected static abstract class ScheduledTask implements Runnable {
        private final long initialDelay;
        private final long period;
        private final TimeUnit timeUnit;
        public ScheduledTask(long initialDelay, long period, TimeUnit timeUnit) {
            this.initialDelay = initialDelay;
            this.period = period;
            this.timeUnit = timeUnit;
        }
    }
}
